package com.my.spring;

import com.my.spring.annotation.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * Created by lenovo on 2018/10/30.
 */
public class WDispatcherServlet extends HttpServlet {

    //加载配置文件
    private Properties contextConfig =new Properties();

    //获取所有类
    private List<String> classNames=new ArrayList<>();

    //ioc容器
    private Map<String,Object> ioc=new HashMap<>();

    //handlerMapping集合
    private List<Handler> handlerMapping =new ArrayList<>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doDispatch(req,resp);
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException {

        try {
            Handler handler = getHandler(req);
            if (handler == null) {
                resp.getWriter().write("404");
                return;
            }

            //所有参数类型
            Class<?> [] paramTypes=handler.method.getParameterTypes();
            //方法需执行的参数数组
            Object [] paramValues=new Object[paramTypes.length];
            //请求参数
            Map<String,String[]> params=req.getParameterMap();
            //循环请求参数
            for (Map.Entry<String,String[]> param:params.entrySet()) {
                //找到请求参数的值
                String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll("", "");

                //如果找到匹配对象，则开始填充参数值
                if (!handler.paramIndexMapping.containsKey(param.getKey())) {
                    continue;
                }
                //找到匹配对象的位置
                int index = handler.paramIndexMapping.get(param.getKey());
                //开始填充值
                paramValues[index] = convert(paramTypes[index], value);

            }

            //请求对象
            int reqIndex=handler.paramIndexMapping.get(HttpServletRequest.class.getName());
            paramValues[reqIndex]=req;
            //响应对象
            int respIndex=handler.paramIndexMapping.get(HttpServletResponse.class.getName());
            paramValues[respIndex]=resp;

            handler.method.invoke(handler.object, paramValues);


        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private Object convert(Class<?> paramType, String value) {
        if(Integer.class.isAssignableFrom(paramType)){
            return Integer.parseInt(value);
        }else {
            return value;
        }
    }

    @Override
    public void init(ServletConfig config) throws ServletException {

        //**************************spring start*******************************
        //1.加载配置文件
        doLoadCofing(config.getInitParameter("contextConfigLocation1"));
        //2.扫描所有相关类
        doScanner(contextConfig.getProperty("scan-package").toString());
        //3.实例化所有相关类
        doInstance();
        //4.自动依赖注入属性
        doAutowrid();
        //**************************spring end*******************************

        //**************************spring mvc*******************************
        //5.初始化HandlerMapping
        initHandlerMapping();

        System.out.println("初始化完成");


    }

    //初始化HandlerMapping
    private void initHandlerMapping() {
        if(ioc.isEmpty()){
            return;
        }
        //1。扫描容器
        for(Map.Entry<String,Object> entry : ioc.entrySet()){

            //获取对象类
            Class<?> clazz = entry.getValue().getClass();

            //是否为Controller类
            if(!clazz.isAnnotationPresent(WController.class)){
                continue;
            }


            WRequestMapping wRequestMapping=clazz.getAnnotation(WRequestMapping.class);
            String baseUrl="";
            if(wRequestMapping!=null){
                baseUrl=wRequestMapping.value();
            }

            for(Method method : clazz.getMethods()) {
                if (!method.isAnnotationPresent(WRequestMapping.class)) {
                    continue;
                }

                WRequestMapping w = method.getAnnotation(WRequestMapping.class);
                String methodUrl = "/" + baseUrl + w.value();
                methodUrl=methodUrl.replaceAll("/+", "/");

                Handler handler=new Handler(methodUrl,method,entry.getValue());
                handlerMapping.add(handler);

                System.out.println("Mapping: " + methodUrl + " " + method);
            }

        }

    }

    //自动依赖注入属性
    private void doAutowrid() {
        if(ioc.isEmpty()){
            return;
        }
        //循环ioc容器，将需要注入的字段，都绑定上
        for(Map.Entry<String,Object> entry : ioc.entrySet()){
            //循环当前ioc容器中对象的所有字段
            Field [] fields= entry.getValue().getClass().getDeclaredFields();
            for (Field f : fields){
                //过滤不需要处理的字段
                if(!f.isAnnotationPresent(WAutowired.class)){
                    continue;
                }
                //获取需要注入的接口名称，该接口对应的实现类对象肯定也在ioc容器中
                WAutowired autowired=f.getAnnotation(WAutowired.class);
                String beanName =autowired.value().trim();
                //没有自定义名字，获取当前字段类型名称
                if(beanName.equals("")){
                    beanName=f.getType().getSimpleName();
                }

                //暴力赋值
                f.setAccessible(true);

                try {
                    //给字段赋值，第一个参数：赋值字段所在的对象，第二个字段：获取字段的value值
                    f.set(entry.getValue(),ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }
    }

    //实例化对象
    private void doInstance() {
        if (classNames.isEmpty()) {
            return;
        }

        for (String className : classNames) {
            try {
                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(WController.class)) {
                    String key = lowerFirst(clazz.getSimpleName());
                    Object value = clazz.newInstance();
                    ioc.put(key, value);
                } else if (clazz.isAnnotationPresent(WService.class)) {
                    //1.默认采用首字符
                    //2.如果自定义了名字，采用自定义的名字
                    //3.根据接口类型来赋值
                    WService wService = clazz.getAnnotation(WService.class);
                    String key = wService.value();
                    if (key.equals("")) {
                        key = lowerFirst(clazz.getSimpleName());
                    }

                    Object value = clazz.newInstance();
                    ioc.put(key, value);

                    for (Class<?> c : clazz.getInterfaces()) {
                        ioc.put(c.getSimpleName(), value);
                    }

                } else {
                    continue;
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    //找到所有类
    private void doScanner(String scanPackage) {
        // this.getClass().getClassLoader().getResource() 类路径
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "//"));
        File classDir=new File(url.getFile());

        for(File file:classDir.listFiles()) {
            if (file.isDirectory()) {
                doScanner(scanPackage + "." + file.getName());
            }else{
                String className=scanPackage+"." + file.getName().replace(".class","");
                classNames.add(className);
            }
        }

    }

    //加载配置文件
    private void doLoadCofing(String contextConfigLocation) {
        InputStream in= this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            contextConfig.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(null != in ){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private String lowerFirst(String name ){
        char [] chars=name.toCharArray();
        chars[0] +=32;
        return String.valueOf(chars);
    }

    //获取handlerMapping
    private Handler getHandler(HttpServletRequest req){
        if(handlerMapping.isEmpty()){
            return null;
        }

        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");

        for(Handler h : handlerMapping){
            if(!h.url.equals(url)){
                continue;
            }
            return h;
        }

        return null;
    }


    public class Handler{
        private String url;
        private Method method;
        private Object object;

        private Map<String,Integer> paramIndexMapping;

        public Handler(String url,Method method,Object object){
            this.url=url;
            this.method=method;
            this.object=object;

            paramIndexMapping=new HashMap<>();
            putParamIndexMapping(method);
        }


        private void putParamIndexMapping(Method method){

            //提取方法中加了注解的参数
            Annotation [][] pa =method.getParameterAnnotations();
            for(int i=0;i<pa.length;i++) {
                for (Annotation a : pa[i]) {
                    if (a instanceof WRequestParam) {
                        String paramName = ((WRequestParam) a).value();
                        if (!paramName.equals("")) {
                            paramIndexMapping.put(paramName, i);
                        }
                    }
                }
            }


            //提取方法中的request和response参数
            Class<?> [] paramsTypes=method.getParameterTypes();
            for(int i=0; i < paramsTypes.length;i++) {
                Class<?> type=paramsTypes[i];
                if(type == HttpServletRequest.class || type == HttpServletResponse.class){
                    paramIndexMapping.put(type.getName(),i);
                }
            }
        }
    }
}
